﻿using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Windows.Forms;
using ZXing.Common;

namespace UDIA
{
    public partial class FrmMain : Form
    {
        static readonly string AppPath = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
        readonly string WechatModelPath = Path.Combine(AppPath, "oepcvQR");

        private WeChatQRCode wechatQrcode = null; //opencv 微信解析二维码
        public FrmMain()
        {
            InitializeComponent();

            wechatQrcode = OpenCvSharp.WeChatQRCode.Create(
                        Path.Combine(WechatModelPath, "detect.prototxt"),
                        Path.Combine(WechatModelPath, "detect.caffemodel"),
                        Path.Combine(WechatModelPath, "sr.prototxt"),
                        Path.Combine(WechatModelPath, "sr.caffemodel"));

        }

        private void CVpic(string picPath)
        {
            Mat img = Cv2.ImRead(picPath);

            //Cv2.b
            Cv2.GetRotationMatrix2D(new Point2f(img.Width / 2, img.Height / 2), -90, 1.0);
            Mat copy = new Mat();
            img.CopyTo(copy);

            //灰度处理
            Mat gray = new Mat();
            Cv2.CvtColor(copy, gray, ColorConversionCodes.RGB2GRAY);

            //2  高斯滤波---X轴方向进行索贝尔求导--二值化
            Cv2.GaussianBlur(gray, gray, new OpenCvSharp.Size(3, 3), 0, 0);

            //3 开操作-用于去除小的对象
            //4 闭操作--用于填充小孔，使条形码连接成一个整体
            //5 寻找轮廓并进行筛选
            Mat canny = new Mat();
            Cv2.Sobel(gray, canny, MatType.CV_32FC1, 1, 0, 3);
            Cv2.ConvertScaleAbs(canny, canny);
            Cv2.Blur(canny, canny, new OpenCvSharp.Size(5, 5));
            Cv2.Threshold(canny, canny, 40, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);


            Mat resizedMat = new Mat();
            Cv2.Resize(canny, resizedMat, new OpenCvSharp.Size(0, 0), 0.3, 0.3);
            Cv2.ImShow("win1", resizedMat);
            Cv2.WaitKey(0);
        }

        private void CVimg(string picPath)
        {
            Mat image, imageGray = new Mat(), imageGuussian = new Mat();
            Mat imageSobelX = new Mat(), imageSobelY = new Mat(), imageSobelOut = new Mat();
            image = Cv2.ImRead(picPath);

            //var m = Cv2.GetRotationMatrix2D(new Point2f(image.Width / 2, image.Height / 2), 90, 1.0);
            //var sizeOld = image.Size();
            //Cv2.WarpAffine(image, image, m, new OpenCvSharp.Size(sizeOld.Height, sizeOld.Width));

            int width = 800;
            int hight = Convert.ToInt32((double)image.Height / ((double)image.Width / width));

            //1. 原图像大小调整，提高运算效率
            Cv2.Resize(image, image, new OpenCvSharp.Size(width, hight));
            ShowCV("1.原图像", image);

            //2. 转化为灰度图
            Cv2.CvtColor(image, imageGray, ColorConversionCodes.RGB2GRAY);
            ShowCV("2.灰度图", imageGray);

            //3. 高斯平滑滤波
            Cv2.GaussianBlur(imageGray, imageGuussian, new OpenCvSharp.Size(3, 3), 0);
            ShowCV("3.高斯平衡滤波", imageGuussian);

            //4.求得水平和垂直方向灰度图像的梯度差,使用Sobel算子
            Mat imageX16S = new Mat(), imageY16S = new Mat();
            Cv2.Sobel(imageGuussian, imageX16S, MatType.CV_16S, 1, 0, 3, 1, 0, BorderTypes.Reflect101);
            Cv2.Sobel(imageGuussian, imageY16S, MatType.CV_16S, 0, 1, 3, 1, 0, BorderTypes.Reflect101);
            Cv2.ConvertScaleAbs(imageX16S, imageSobelX, 1, 0);
            Cv2.ConvertScaleAbs(imageY16S, imageSobelY, 1, 0);
            imageSobelOut = imageSobelY - imageSobelX;
            ShowCV("4.X方向梯度", imageSobelX);
            ShowCV("4.Y方向梯度", imageSobelY);
            ShowCV("4.XY方向梯度差", imageSobelOut);

            //5.均值滤波，消除高频噪声
            Cv2.Blur(imageSobelOut, imageSobelOut, new OpenCvSharp.Size(2, 1));
            ShowCV("5.均值滤波", imageSobelOut);

            //6.二值化
            Mat imageSobleOutThreshold = new Mat();
            Cv2.Threshold(imageSobelOut, imageSobleOutThreshold, 180, 255, ThresholdTypes.Binary);//CV_THRESH_BINARY
            ShowCV("6.二值化", imageSobleOutThreshold);

            //7.闭运算，填充条形码间隙
            Mat element = Cv2.GetStructuringElement(0, new OpenCvSharp.Size(7, 7));
            Cv2.MorphologyEx(imageSobleOutThreshold, imageSobleOutThreshold, MorphTypes.Close, element);//MORPH_CLOSE
            ShowCV("7.闭运算", imageSobleOutThreshold);

            //8. 腐蚀，去除孤立的点
            Cv2.Erode(imageSobleOutThreshold, imageSobleOutThreshold, element);
            ShowCV("8.腐蚀", imageSobleOutThreshold);

            //9. 膨胀，填充条形码间空隙，根据核的大小，有可能需要2~3次膨胀操作
            Cv2.Dilate(imageSobleOutThreshold, imageSobleOutThreshold, element);
            Cv2.Dilate(imageSobleOutThreshold, imageSobleOutThreshold, element);
            Cv2.Dilate(imageSobleOutThreshold, imageSobleOutThreshold, element);
            ShowCV("9.膨胀", imageSobleOutThreshold);

            OpenCvSharp.Point[][] contours;
            OpenCvSharp.HierarchyIndex[] hiera;

            //10.通过findContours找到条形码区域的矩形边界 CV_RETR_EXTERNAL CV_CHAIN_APPROX_NONE
            Cv2.FindContours(imageSobleOutThreshold, out contours, out hiera, RetrievalModes.External, ContourApproximationModes.ApproxNone);

            List<string> res = new List<string>();
            for (int i = 0; i < contours.Length; i++)
            {
                Rect rect = Cv2.BoundingRect(contours[i]);
                if (rect.Width * rect.Height > 6000)
                {
                    int exRange = 20;
                    rect.X -= exRange;
                    if (rect.X < 0) rect.X = 0;
                    rect.Y -= exRange;
                    if (rect.Y < 0) rect.Y = 0;
                    rect.Width += exRange * 2;
                    if (rect.X + rect.Width > image.Width) rect.Width = image.Width - rect.X;
                    rect.Height += exRange * 2;
                    if (rect.Y + rect.Height > image.Height) rect.Height = image.Height - rect.Y;

                    Cv2.Rectangle(image, rect, new Scalar(255), 2);
                    //var cut = new Mat(image, rect);
                    //var code = GetZXingRes(cut);
                    //res.Add(code);
                    //Cv2.ImShow(i.ToString(), cut);
                }
            }
            Cv2.ImShow("10.找出二维码矩形区域", image);
            //MessageBox.Show(string.Join("\r\n", res));

            Cv2.WaitKey();
        }

        bool CanShow = true;

        private void ShowCV(string title, Mat img)
        {
            if (CanShow)
                Cv2.ImShow(title, img);
        }

        //通过图片地址zxing解析128码
        private List<string> GetZXingRes(string path)
        {
            List<string> list = new List<string>();
            try
            {
                Bitmap img = new Bitmap(path);
                DecodingOptions decodeOption = new DecodingOptions();
                ZXing.BarcodeReader reader = new ZXing.BarcodeReader
                {
                    Options = decodeOption
                };
                var result = reader.DecodeMultiple(img);
                list = result.Select(t => t.Text).ToList();
                //labelBarCodeResult.Text = "识别结果是:" + barCodeStr;
                img.Dispose();
            }
            catch
            { }

            return list;
        }

        private string GetZXingRes(Mat cut)
        {
            try
            {
                Bitmap img = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(cut);
                DecodingOptions decodeOption = new DecodingOptions();
                ZXing.BarcodeReader reader = new ZXing.BarcodeReader
                {
                    Options = decodeOption
                };
                var result = reader.Decode(img);

                string res = result?.Text;
                img.Dispose();
                return res;
            }
            catch
            {
                return null;
            }

        }


        private List<string> GetScanRes(string picPath)
        {
            var src = Cv2.ImRead(picPath);
            List<string> list = new List<string>();
            try
            {
                wechatQrcode.DetectAndDecode(src, out var rects, out var texts);
                list.AddRange(texts);
            }
            catch (Exception)
            {

            }
            src.Dispose();
            GC.Collect();
            return list;
        }

        private void btnChoose_Click(object sender, EventArgs e)
        {
            OpenFileDialog open = new OpenFileDialog();
            //选择图片
            if (open.ShowDialog() == DialogResult.OK)
            {
                var picPath = open.FileName;

                var label = GetZXingRes(picPath);

                var qr = GetScanRes(picPath);

                var codes = label.Union(qr).ToList();

                MessageBox.Show(string.Join("\r-------------------\r", codes));

            }
        }

        private void btnCV_Click(object sender, EventArgs e)
        {
            OpenFileDialog open = new OpenFileDialog();
            if (open.ShowDialog() == DialogResult.OK)
            {
                //CVpic(open.FileName);
                CVimg(open.FileName);
            }
        }
    }
}
